package main

import (
	"fmt"
	"math/rand"
	"time"
)

/*
有多个通道，现在需要多路复用地从多个通道接收数据

如果不使用select，就需要写一个死循环，并尝试从多个通道获取数据
for{
    // 尝试从ch1接收值
    data, ok := <-ch1
    // 尝试从ch2接收值
    data, ok := <-ch2
    …
}
这样性能会很低

如果使用select结合case，可以很方便地实现多路复用获取数据
 select {
    case <-chan1:
       // 如果chan1成功读到数据，则进行该case处理语句
    case chan2 <- 1:
       // 如果成功向chan2写入数据，则进行该case处理语句
    default:
       // 如果上面都没有成功，则进入default处理流程
    }

select可以
	同时监听一个或多个channel，直到其中一个channel ready
	如果多个channel同时ready，则随机选择一个执行
*/

func main() {

	//demo01SelectSimple()
	//demo02LoopSelect()
	demo03SelectWriteChannelRange()
}

/*
s1= test1

*/
func demo01SelectSimple() {
	// 2个管道
	output1 := make(chan string)
	output2 := make(chan string)
	// 跑2个子协程，写数据
	go test1(output1)
	go test2(output2)
	// 用select监控
	// 此处select发现两个通道都ready了，就随机挑选一个执行
	select {
	case s1 := <-output1:
		fmt.Println("s1=", s1)
	case s2 := <-output2:
		fmt.Println("s2=", s2)
	}
	time.Sleep(5 * time.Second)
}
func test1(ch chan string) {
	ch <- "test1"
}
func test2(ch chan string) {
	ch <- "test2"
}

func demo02LoopSelect() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	fmt.Printf("%T %T\n", ch1, ch2)
	// 启动一个协程来不停从两个通道读取数据
	go func() {
		// 一个死循环，不停的读数据
		for {
			select {
			case item1 := <-ch1:
				fmt.Println("item1=", item1)
			case item2 := <-ch2:
				fmt.Println("item2=", item2)
			default:
				fmt.Println("nothing happened!!")
			}
			time.Sleep(200 * time.Millisecond)
		}

	}()

	// 启动两个协程，分别不停往两个channel中发送数据
	go func() {
		for i := 0; i < 10; i++ {
			rand.Seed(time.Now().UnixNano())
			ch1 <- rand.Intn(10) // 生成[0, 10)之间随机数
			time.Sleep(1000 * time.Millisecond)
		}
	}()

	go func() {
		for i := 0; i < 10; i++ {
			rand.Seed(time.Now().UnixNano())
			ch2 <- rand.Intn(10) + 10 // 生成[10, 20)之间随机数
			time.Sleep(400 * time.Millisecond)
		}
	}()

	// 主协程等待20秒
	time.Sleep(20 * time.Second)
}

/*
select + case可以实现往通道写数据
在主函数中使用range channel来不停地获取数据
*/
func demo03SelectWriteChannelRange() {
	// 创建管道
	output1 := make(chan string, 10)
	// 子协程写数据
	go write0(output1)
	// 取数据
	for s := range output1 {
		fmt.Println("res:", s)
		time.Sleep(time.Second)
	}
}

func write0(ch chan string) {
	for {
		select {
		// 写数据
		case ch <- "hello":
			fmt.Println("write hello")
		default:
			fmt.Println("channel full")
		}
		time.Sleep(time.Millisecond * 100)
	}
}
